/*
 *	               _ooOoo_ 
 *	              o8888888o 
 *	              88" . "88 
 *	              (| -_- |) 
 *	              O\  =  /O 
 *	           ____/`---'\____ 
 *	         .'  \\|     |//  `. 
 *	        /  \\|||  :  |||//  \ 
 *	       /  _||||| -:- |||||-  \ 
 *	       |   | \\\  -  /// |   | 
 * 	       | \_|  ''\---/''  |   | 
 * 	       \  .-\__  `-`  ___/-. / 
 * 	    ___ `. .'  /--.--\  `. . __ 
 *	  ."" '<  `.___\_<|>_/___.'  >'"". 
 *	 | | :  `- \`.;`\ _ /`;.`/ - ` : | | 
 *	 \  \ `-.   \_ __\ /__ _/   .-` /  / 
 *======`-.____`-.___\_____/___.-`____.-'====== 
 *                   `=---=' 
 *^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
 *佛祖保佑       永无BUG 
 * File name:        PointUtil.java
 * Copyright@blog.ilvelh.com(China)
 * Editor:           JDK1.7_40
 */
package com.ilvelh.tools;

import java.io.Serializable;
import java.util.List;

/**
 * 坐标工具类
 * <p>
 * Author: ilveh
 * <p>
 * Date:2013-7-22 Time:下午3:21:21
 * <p>
 */
public class PointUtil {
	private final static double EARTH_RADIUS_l = 6378.137; // 地球长半径
	private final static double EARTH_RADIUS_s = 6356.755; // 地球短半径
	// 地球平均半径
	private final static double EARTH_RADIUS = (EARTH_RADIUS_l + EARTH_RADIUS_s) / 2;

	private static double rad(double d) {
		return d * Math.PI / 180.0;
	}

	/**
	 * 计算两个坐标点之间的距离
	 * 
	 * @param coordinate1
	 *            Coordinate
	 * @param coordinate2
	 *            Coordinate
	 * @return double
	 */
	public static double getDistance(Coordinate coordinate1,
			Coordinate coordinate2) {
		if (null == coordinate1 || null == coordinate2) {
			return -1;
		}
		double radLat1 = rad(coordinate1.getLat());
		double radLat2 = rad(coordinate2.getLat());
		double a = radLat1 - radLat2;
		double b = rad(coordinate1.getLng()) - rad(coordinate2.getLng());
		double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
				+ Math.cos(radLat1) * Math.cos(radLat2)
				* Math.pow(Math.sin(b / 2), 2)));
		s = s * EARTH_RADIUS;
		s = (s * 10000 + 0.5) / 10;
		return s;
	}

	/**
	 * 通过坐标和四个点 转换成在屏幕上展示的像素
	 * 
	 * @param coordinate
	 *            Coordinate
	 * @param view
	 *            LatLngView 四个点坐标数据 (默认该四点为长方形,因为在计算的时候只获取了3个点坐标)
	 * @param pixelX
	 *            屏幕的宽度
	 * @param pixelY
	 *            屏幕的高度
	 * @return double[] 返回坐标转换成屏幕像素的坐标
	 */
	public static double[] getPixel(Coordinate coordinate, LatLngView view,
			int pixelX, int pixelY) {
		// 初始化页面像素
		double[] pixel = { -10000, -10000 };
		// 如果该坐标不在范围
		Coordinate rt = new Coordinate(view.getRtlng(), view.getRtlat());
		// rb rightbuttom
		Coordinate rb = new Coordinate(view.getRblng(), view.getRblat());
		// lt lefttop
		Coordinate lt = new Coordinate(view.getLtlng(), view.getLtlat());
		// lb leftbuttom
		Coordinate lb = new Coordinate(view.getLblng(), view.getLblat());
		if (coordinate == null || !isInRegion(coordinate, rt, rb, lt, lb)) {
			return pixel;
		}
		double a = getDistance(coordinate, rb);
		double c = getDistance(coordinate, lt);
		double d = getDistance(coordinate, lb);
		double b = getDistance(rb, lt);
		double e = getDistance(lt, lb);

		double tempLng = (int) ((b * b + c * c - a * a) / (2 * b));
		double tempLat = (int) ((e * e + c * c - d * d) / (2 * e));

		tempLng = (int) ((tempLng / b) * pixelY);
		tempLat = (int) ((tempLat / e) * pixelX);

		pixel[0] = tempLat;
		pixel[1] = tempLng;
		return pixel;
	}

	/**
	 * 校验某一点坐标是否在几个点坐标之内
	 * 
	 * @param coordinate
	 *            Coordinate
	 * @param coordinates
	 *            List<Coordinate>
	 * @return boolean
	 */
	public static boolean isInRegionWithPoint(Coordinate coordinate,
			List<Coordinate> coordinates) {
		if (coordinates == null)
			return false;
		// 经研究发现toArray有两个方法：
		// public Object[] toArray() {
		// Object[] result = new Object[size];
		// System.arraycopy(elementData, 0, result, 0, size);
		// return result;
		// }
		// 不带参数的toArray方法，是构造的一个Object数组， 然后进行数据拷贝，
		// 此时进行转型就会产生ClassCastException。
		Coordinate[] coordinateArray = (Coordinate[]) coordinates
				.toArray(new Coordinate[0]);
		return isInRegion(coordinate, coordinateArray);
	}

	/**
	 * 校验某一点坐标是否在几个点坐标之内
	 * 
	 * @param pt
	 *            被校验坐标
	 * @param coordinates
	 *            多点坐标
	 * @return 返回是否在几个点坐标之内
	 */
	public static boolean isInRegion(Coordinate pt, Coordinate... coordinates) {
		int jd = 0;
		if (pt == null) {
			return false;
		}
		int size = coordinates.length;
		for (int i = 0; i < size; i++) {
			Coordinate p1 = coordinates[i];
			if (p1 == null) {
				return false;
			}
			Coordinate p2 = coordinates[(i + 1) % size];
			if (p2 == null) {
				return false;
			}
			// 校验算法
			if (p1.getLng() - p2.getLng() == 0) {
				continue;
			}
			if (p1.getLng() > pt.getLng() && p2.getLng() >= pt.getLng()) {
				continue;
			}
			if (p1.getLng() < pt.getLng() && p2.getLng() <= pt.getLng()) {
				continue;
			}
			if (p1.getLat() < pt.getLat() && p2.getLat() < pt.getLat()) {
				continue;
			}
			double lng_tmp = p1.getLat() - (p1.getLat() - p2.getLat())
					* (p1.getLng() - pt.getLng()) / (p1.getLng() - p2.getLng());
			if (lng_tmp > pt.getLat()) {
				jd++;
			}

		}
		if ((jd & 1) == 1) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 判断坐标点是否在俱乐部范围内
	 * 
	 * @param coordinate
	 *            Coordinate
	 * @param view
	 *            LatLngView
	 * @return boolean
	 */
	public static boolean isInClub(Coordinate coordinate, LatLngView view) {
		if (coordinate == null || view == null)
			return false;
		Coordinate rt = new Coordinate(view.getRtlng(), view.getRtlat());
		Coordinate rb = new Coordinate(view.getRblng(), view.getRblat());
		Coordinate lt = new Coordinate(view.getLtlng(), view.getLtlat());
		Coordinate lb = new Coordinate(view.getLblng(), view.getLblat());
		return isInRegion(coordinate, rt, rb, lt, lb);
	}

	public static class Coordinate implements Serializable {
		/**
		 * 
		 */
		private static final long serialVersionUID = -7297562603315916261L;

		/**
		 * 经度X
		 */
		private double lng;
		/**
		 * 纬度Y
		 */
		private double lat;

		/**
		 * 
		 */
		public Coordinate() {
			super();
		}

		/**
		 * @param lng
		 * @param lat
		 */
		public Coordinate(double lng, double lat) {
			super();
			this.lng = lng;
			this.lat = lat;
		}

		/**
		 * @return the lng
		 */
		public double getLng() {
			return lng;
		}

		/**
		 * @param lng
		 *            the lng to set
		 */
		public void setLng(double lng) {
			this.lng = lng;
		}

		/**
		 * @return the lat
		 */
		public double getLat() {
			return lat;
		}

		/**
		 * @param lat
		 *            the lat to set
		 */
		public void setLat(double lat) {
			this.lat = lat;
		}

	}

	public static class LatLngView {

		// 左上经纬度
		private double ltlng;
		private double ltlat;
		// 左下经纬度
		private double lblng;
		private double lblat;
		// 右上经纬度
		private double rtlng;
		private double rtlat;
		// 右下经纬度
		private double rblng;
		private double rblat;

		public LatLngView() {
		}

		public LatLngView(double ltlng, double ltlat, double lblng,
				double lblat, double rtlng, double rtlat, double rblng,
				double rblat) {
			super();
			this.ltlng = ltlng;
			this.ltlat = ltlat;
			this.lblng = lblng;
			this.lblat = lblat;
			this.rtlng = rtlng;
			this.rtlat = rtlat;
			this.rblng = rblng;
			this.rblat = rblat;
		}

		/**
		 * @return the ltlng
		 */
		public double getLtlng() {
			return ltlng;
		}

		/**
		 * @param ltlng
		 *            the ltlng to set
		 */
		public void setLtlng(double ltlng) {
			this.ltlng = ltlng;
		}

		/**
		 * @return the ltlat
		 */
		public double getLtlat() {
			return ltlat;
		}

		/**
		 * @param ltlat
		 *            the ltlat to set
		 */
		public void setLtlat(double ltlat) {
			this.ltlat = ltlat;
		}

		/**
		 * @return the lblng
		 */
		public double getLblng() {
			return lblng;
		}

		/**
		 * @param lblng
		 *            the lblng to set
		 */
		public void setLblng(double lblng) {
			this.lblng = lblng;
		}

		/**
		 * @return the lblat
		 */
		public double getLblat() {
			return lblat;
		}

		/**
		 * @param lblat
		 *            the lblat to set
		 */
		public void setLblat(double lblat) {
			this.lblat = lblat;
		}

		/**
		 * @return the rtlng
		 */
		public double getRtlng() {
			return rtlng;
		}

		/**
		 * @param rtlng
		 *            the rtlng to set
		 */
		public void setRtlng(double rtlng) {
			this.rtlng = rtlng;
		}

		/**
		 * @return the rtlat
		 */
		public double getRtlat() {
			return rtlat;
		}

		/**
		 * @param rtlat
		 *            the rtlat to set
		 */
		public void setRtlat(double rtlat) {
			this.rtlat = rtlat;
		}

		/**
		 * @return the rblng
		 */
		public double getRblng() {
			return rblng;
		}

		/**
		 * @param rblng
		 *            the rblng to set
		 */
		public void setRblng(double rblng) {
			this.rblng = rblng;
		}

		/**
		 * @return the rblat
		 */
		public double getRblat() {
			return rblat;
		}

		/**
		 * @param rblat
		 *            the rblat to set
		 */
		public void setRblat(double rblat) {
			this.rblat = rblat;
		}

	}

	/**
	 * 有些坐标需要通过转换才可以使用
	 * 
	 * @param coordinate
	 * @return
	 */
	public static Coordinate convert(Coordinate coordinate) {
		double lng = coordinate.getLng();
		double lat = coordinate.getLat();
		lng = Math.round(lng) + (lng - Math.round(lng)) / 0.6;
		lat = Math.round(lat) + (lat - Math.round(lat)) / 0.6;
		coordinate.setLat(lat);
		coordinate.setLng(lng);
		return coordinate;
	}

}
